/* Copyright 2005 Sony Mathew, Minneapolis MN. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package com.xsm.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.Properties;

/**
 * @author Sony Mathew
 */
public class IOUtil {

	public static final int COPY_BYTES_BUFFER_BYTES = 1024*4;
    public static final String LINE_SEP = System.getProperty("line.separator");
    
    /**
     * author: Sony Mathew
     * 
     * @see #readProperties(Class, String)
     */
    public static Properties readPropertiesResource(String resource) 
	{
    	return readPropertiesResource(IOUtil.class, resource);
	}

    /**
     * author: Sony Mathew
     * 
     * Obtains the "whole" data from the given resource as a single String.
     * Reads the resource using the classloader of the given class.
     * 
     * @throws RuntimeException
     */
    public static Properties readPropertiesResource(Class clazz, String resource) 
	{
    	InputStream in = null;
    	try {
	    	in = clazz.getResourceAsStream(resource);
	    	if (in == null) {
	    		throw new RuntimeException("Not found in classpath [" + resource + "]");
	    	}
	    		    	
	        try {
	        	Properties properties = new Properties();	        	
	            properties.load(in);
				return properties;	        	            
	        } catch (IOException e) {
	            throw new RuntimeException("Error reading resource [" + resource + "]", e);
	        }	        
    	}finally{
            close(in);    		
    	}
    }

    /**
     * author: Sony Mathew
     * 
     * @see #readStringResource(Class, String) 
     */
    public static String readStringResource(String resource) 
    	throws RuntimeException, RuntimeException 
	{
    	return readStringResource(IOUtil.class, resource);
	}
    
    /**
     * author: Sony Mathew
     * 
     * Obtains the "whole" data from the given resource as a single String.
     * Reads the resource using the classloader of the given class.
     * 
     * @throws RuntimeException
     */
    public static String readStringResource(Class clazz, String resource) 
    	throws RuntimeException, RuntimeException 
	{
    	InputStream in = null;
    	try {
	    	in = clazz.getResourceAsStream(resource);
	    	if (in == null) {
                throw new RuntimeException("Not found in classpath [" + resource + "]");
	    	}    	
	    	return readString(in);    	
    	}finally{
    		close(in);
    	}
    }
	
    /**
     * author: Sony Mathew
     * 
     * Obtains the "whole" data from the given InputStrea  as a single String.
     * @throws RuntimeException
     */
    public static String readString(InputStream in) throws RuntimeException {
    	return readString(new InputStreamReader(in));
    }
	
    /**
     * author: Sony Mathew
     * 
     * Obtains the "whole" data from the given Reader as a single String.
     * @throws RuntimeException
     */
    public static String readString(Reader r) throws RuntimeException {
        if (r instanceof StringReader) {            
            return ((StringReader)r).toString();
        }
                
        BufferedReader buffReader = new BufferedReader(r);        
        StringBuffer msg = new StringBuffer();
        try {
        	String line=null;
			for(int i=0; (line = buffReader.readLine()) != null; i++) {
				if (i>0) {
					msg.append(LINE_SEP);
				}
				msg.append(line);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
        return msg.toString();
    }    

    /**
     * Writes the given resource read using the given IOUtil class to the given out.
     *  
     * <br/>author Sony Mathew
     */
    public static void copyResource(String resource, OutputStream out) throws RuntimeException {
        copyResource(IOUtil.class, resource, out);
    }
    
    /**
     * Writes the given resource read using the given clazz to the given out.
     *  
     * <br/>author Sony Mathew
     */
    public static void copyResource(Class clazz, String resource, OutputStream out) throws RuntimeException {
        InputStream in = clazz.getResourceAsStream(resource);
    	if (in == null) {
            throw new RuntimeException("Not found in classpath [" + resource + "]");
    	}
    	
        try {
            copyBytes(in, out);
        }finally{
            close(in);
        }
    }
    
    /**
     * author: Sony Mathew
     * 
     * Transfer bytes from in to out
     * @throws RuntimeException
     */
    public static void copyBytes(InputStream in, OutputStream out) throws RuntimeException {
        try {
	        byte[] buf = new byte[COPY_BYTES_BUFFER_BYTES];
	        int len;
	        while ((len = in.read(buf)) > 0) {
	            out.write(buf, 0, len);
	        }
        }catch(IOException iox) {
            throw new RuntimeException(iox);
        }
    }    
    
    /**
     * Closes the given  stream and supresses the exception (with a stacktrace).
     * Can provide null - will just do-nothing.
     */
    public static void close(OutputStream out) {
        if (out == null) {
            return;
        }
        try {
            out.close();
        } catch (Exception e) {
            warn("Exception druing close of OutputStream", e);
        }
    }

    /**
     * Closes the given stream and supresses the exception (with a stacktrace).
     * Can provide null - will just do-nothing.
     */    
    public static  void close(InputStream in) {
        if (in == null) {
            return;
        }        
        try {
            in.close();
        } catch (Exception e) {
            warn("Exception druing close of InputStream", e);
        }
    }
        
    /**
     * author: Sony Mathew
     * 
     * Read an Input Stream (up to five times faster than copyBytes())
     * @param in - InputStream to process
     * @return StreamResult
     */
    public static StreamResult read(InputStream in) throws RuntimeException {
        StreamResult streamResult = new StreamResult();
        int totalLength = 0;
        try {
			while (true) {
                int available = in.available();
                if (available <= 0) {
                    available = 1;
                }
                
				byte[] segment = new byte[available];                
				int readLen = in.read(segment);
                if (readLen < 0) {
                    break;//we're done. EOF reached.
                }else if (readLen == 0){
                    continue; //read again, perhaps a lag.
                }
                
				if (readLen < segment.length) {
					byte[] newSegment = new byte[readLen];
					System.arraycopy(segment, 0, newSegment, 0, readLen);
					segment = newSegment;
				}
                
				if (readLen != segment.length) {
					throw new IllegalStateException("Read Length [" + readLen +
						"] < segment length [" + segment.length + "]");
				}
                
                if (segment.length > 0) {
    				totalLength += segment.length;								
    				streamResult.addSegment(segment);
                }
			}
            
			streamResult.setLength(totalLength);
			return streamResult;
        }catch(IOException iox) {
            throw new RuntimeException(iox);
        }	
    }
    
    public static void warn(String msg, Throwable x) {
        System.out.println(msg);
        if (x != null) {
            x.printStackTrace();
        }
    }
}
